﻿#pragma once
#include "xop/RtspPusher.h"
#include "libhv/include/hv/json.hpp"
#include "pullStream.h"

#include <thread>
#include <functional>
#include <map>
#include <fstream>

using namespace xop;
using namespace hv;

class pushStream
{
public:
    using Ptr = std::shared_ptr<pushStream>;
    using reStartCB = std::function<void()>;

    pushStream(xop::EventLoop::Ptr eventloop) : _eventLoop(eventloop) {};
    ~pushStream() 
    {
        if (_saveFile) {
            fclose(_file);
        }
        _sendframeThreadStatus = false;
        LOG_DEBUG("pushStream closed ");
    
    } ;

    void setRestartCallback(reStartCB cb) {
        _restartCb = cb;
    }
    void startPush(unsigned long id, StreamSourceList* list, MediaType codeType = MediaType::H265) {
        
        bool status = loadConfig();
        if (_saveFile) {
            _file = fopen(std::to_string(id).append(".h264").c_str(), "wb+");
            if (_file == nullptr)
                LOG_DEBUG("保存文件失败");
        }

        if (!status)
            return;

        std::string port = "554";
        std::string app = "live";
        _rtspUrl = "rtsp://" + _ip + ":" + port + "/" + app + "/" + std::to_string(id);

        _pusher = xop::RtspPusher::Create(_eventLoop.get());

        _session = xop::MediaSession::CreateNew();
        _mediaType = codeType;
        if (MediaType::H265 == codeType) {
            _session->AddSource(xop::channel_0, xop::H265Source::CreateNew());
        } else {
            _session->AddSource(xop::channel_0, xop::H264Source::CreateNew());
        }

        _session->AddSource(xop::channel_1, xop::G711ASource::CreateNew());
        _session->AddNotifyDisconnectedCallback([&](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) {
            LOG_DEBUG("disconnected ip :%s port:%d", peer_ip.c_str(), peer_port);
            _restartCb();
        });
        _session->AddNotifyConnectedCallback([](xop::MediaSessionId sessionId, std::string peer_ip, uint16_t peer_port) {
            LOG_DEBUG("connected ip :%s port:%d", peer_ip.c_str(), peer_port);
        });

        _pusher->AddSession(_session);
        if (!_pusher->OpenUrl(_rtspUrl))
            LOG_DEBUG("open rtsp:%s  error", _rtspUrl.data());

        LOG_DEBUG("start push to  URL: %s ", _rtspUrl.data());

        std::thread send(&pushStream::SendFrameThread, this, _pusher, list);
        send.detach();
        _sendframeThreadStatus = true;
    }
    std::string getStreamUrl() {
        return _rtspUrl;
    }
private:

    void SendFrameThread(xop::RtspPusher::Ptr rtsp_server, StreamSourceList* h264_file)
    {
        xop::AVFrame frame{};
        auto result = false;
        while (_sendframeThreadStatus) {
            result = h264_file->getFrame(frame);
            if (frame.size > 0 && result) {
                if (frame.type == xop::FrameType::AUDIO_FRAME) {
                    result = rtsp_server->PushFrame(xop::channel_1, frame);
                } else {
                    //if (_mediaType != frame.protocal) {
                    //    _mediaType = frame.protocal;
                    //    _session->RemoveSource(xop::channel_0);
                    //    LOG_DEBUG("切换编码协议当前为:%d %x", frame.protocal, this);
                    //    if (frame.protocal == MediaType::H264) {
                    //        _session->AddSource(xop::channel_0, xop::H264Source::CreateNew());
                    //    } else {
                    //        _session->AddSource(xop::channel_0, xop::H265Source::CreateNew());
                    //    }
                    //    _pusher->Close();
                    //    _pusher->OpenUrl(_rtspUrl);
                    //}
                    result = rtsp_server->PushFrame(xop::channel_0, frame);
                    if (_saveFile) {
                        fwrite(frame.buffer.get(), frame.size, 1, _file);
                        fflush(_file);
                    }
                }

            } else {
                xop::Timer::Sleep(40);
                continue;
            }

        };
    }
    
    bool loadConfig() {
        std::ifstream ifs;
        ifs.open("config.json", std::ifstream::in);
        if (!ifs.is_open()) {
            LOG_DEBUG("文件路径不存在");
            return false;
        }

        Json root;
        root << ifs;

        _ip = root["streamServerIp"];
        //std::string temp = root["saveFile"];
        //_saveFile = stoi(temp);
        ifs.close();
        return true;
    }

    bool hevcProbe(const xop::AVFrame& p)
    {
        enum hecv_nal {
            HEVC_NAL_VPS = 32,
            HEVC_NAL_SPS = 33,
            HEVC_NAL_PPS = 34,
            HEVC_NAL_AUD = 35,
            HEVC_NAL_BLA_W_LP = 16,
            HEVC_NAL_BLA_W_RADL = 17,
            HEVC_NAL_BLA_N_LP = 18,
            HEVC_NAL_IDR_W_RADL = 19,
            HEVC_NAL_IDR_N_LP = 20,
            HEVC_NAL_CRA_NUT = 21,
        };

        uint32_t code = -1;
        int vps = 0, sps = 0, pps = 0, irap = 0;
        size_t i;

        for (i = 4; i < p.size - 1; ++i) {
            code = (code << 8) + p.buffer.get()[i];
            if ((code & 0xffffff00) == 0x100) {
                uint8_t nal2 = p.buffer.get()[i + 1];
                int type = (code & 0X7E) >> 1;
                if (code & 0x81)
                    return 0;
                if (nal2 & 0xf8)
                    return 0;
                switch (type) {
                case HEVC_NAL_VPS:vps++; break;
                case HEVC_NAL_SPS:sps++; break;
                case HEVC_NAL_PPS:pps++; break;
                case HEVC_NAL_BLA_N_LP:
                case HEVC_NAL_BLA_W_LP:
                case HEVC_NAL_BLA_W_RADL:
                case HEVC_NAL_CRA_NUT:
                case HEVC_NAL_IDR_N_LP:
                case HEVC_NAL_IDR_W_RADL:irap++; break;
                }
            }
        }
        if (vps && sps && pps && irap)
            return   true;
        return false;
    }

    xop::MediaSession* _session{ 0 };
    xop::EventLoop::Ptr  _eventLoop;
    std::string _ip{ "127.0.0.1" };
    MediaType _mediaType = MediaType::H264;
    bool _sendframeThreadStatus = false;
    reStartCB  _restartCb;
    xop::RtspPusher::Ptr _pusher;
    bool _saveFile = false;
    FILE* _file = nullptr;
    std::string _rtspUrl;
} ;

